using UnityEngine;

/// <summary>
/// LivelyCamera 类实现了一个具有“动态响应”能力的摄像机控制器
/// </summary>
public class LivelyCamera : MonoBehaviour
{
    [SerializeField, Min(0f)]
    float
        springStrength = 100f,     // 弹簧力度：控制摄像机被“拉回”到锚点位置的强度，值越大回弹越猛
        dampingStrength = 10f,     // 阻尼力度：控制摄像机运动的阻尼（摩擦力），防止震荡过于剧烈
        jostleStrength = 40f,      // 震动（抖动）强度：当调用 JostleY() 时，摄像机在 Y 轴上瞬间获得的额外推力
        pushStrength = 1f,         // 推动强度：当调用 PushXZ() 时，摄像机在 X/Z 平面上受到的推力比例因子
        maxDeltaTime = 1f / 60f;   // 单次物理步进的最大时间间隔，用于避免因帧率波动造成物理计算不稳定

    Vector3
        anchorPosition,            // 锚点位置：摄像机“想要回归”的目标位置，通常是当前视角的默认或中心位置
        velocity;                  // 摄像机当前的速度向量，用于模拟物理运动
    
    void Awake () 
    {
        // 将摄像机的初始本地位置设为锚点位置，即默认的“稳定点”
        anchorPosition = transform.localPosition;
    }

    // 当需要触发 Y 轴震动效果时调用（比如球未被接住）
    // 该方法会给摄像机 Y 轴方向的速度添加一个瞬时冲击
    public void JostleY () 
    {
        velocity.y += jostleStrength;
    }

    // 当需要给摄像机施加一个 XZ 平面方向的推力时调用（比如球撞击左右边界）
    // impulse 是一个二维向量，通常表示球的速度方向或冲击方向
    public void PushXZ (Vector2 impulse) 
    {
        // 根据传入的冲动向量，在 X 和 Z 方向上分别给摄像机速度添加一个推动力
        velocity.x += pushStrength * impulse.x;
        velocity.z += pushStrength * impulse.y;
    }
    
    void LateUpdate () 
    {
        float dt = Time.deltaTime;  // 获取当前帧的时间间隔

        // 为了避免因为帧率过低导致单帧时间过长，进而造成物理模拟不稳定，
        // 这里将大的 dt 拆分成多个不超过 maxDeltaTime 的小步长，逐步计算
        while (dt > maxDeltaTime) 
        {
            TimeStep(maxDeltaTime);  // 执行一次固定时间步长的物理更新
            dt -= maxDeltaTime;      // 减去已经处理的时间
        }

        // 处理剩余不足一个 maxDeltaTime 的时间
        TimeStep(dt);
    }

    // 执行单步物理模拟，根据弹簧-阻尼模型更新摄像机的位置和速度
    // dt：当前这一小步的时间间隔
    void TimeStep (float dt) 
    {
        // 计算当前摄像机位置与锚点位置之间的“偏移量”（即偏离理想位置的距离）
        Vector3 displacement = anchorPosition - transform.localPosition;

        // 根据胡克定律和阻尼原理，计算当前帧的加速度：
        // - springStrength * displacement：弹簧拉力，总是试图将相机拉回 anchorPosition
        // - dampingStrength * velocity：阻尼力，与当前速度相反，用于抑制震荡
        Vector3 acceleration = springStrength * displacement - dampingStrength * velocity;

        // 根据加速度更新速度：v = v + a * dt
        velocity += acceleration * dt;

        // 根据速度更新位置：p = p + v * dt，实现平滑移动
        transform.localPosition += velocity * dt;
    }
}